package com.liu.yubi.algorithm;


import java.util.concurrent.atomic.AtomicLong;

/**
 * @author lqh
 * @since 2025-05-31 23:00
 * 固定窗口限流算法
 */
public class FixedAlgorithm {
    /**
     * 窗口大小（ms）
     */
    private final Long windowSizeMs;

    /**
     * 阀值（窗口内最大请求次数）
     */
    private final Integer requestMax;

    /**
     * 窗口开始时间
     */
    private final AtomicLong windowStartTime;

    /**
     * 计数器（计算窗口时间请求次数）
     */
    private final AtomicLong count;

    public FixedAlgorithm(Long windowSizeMs,Integer requestMax) {
        this.windowSizeMs = windowSizeMs;
        this.requestMax = requestMax;
        this.windowStartTime = new AtomicLong(System.currentTimeMillis());
        //初始化计数器为0
        this.count = new AtomicLong(0);
    }

    /**
     * 尝试访问获取令牌（窗口时间内限制的请求次数）
     * return true:OK   false:NO
     */
    public boolean tryAcquire(){
        //获取当前时间（ms）
        long now = System.currentTimeMillis ();
        //判断起始时间是在窗口时间内
        if (now-windowStartTime.get() > windowSizeMs){
            //重置窗口
            resetTime(now);
        }
        //新增一次窗口请求
        long counter = count.incrementAndGet ();
        //判断是否在阀值范围内
        if (counter <= requestMax){
            return true;
        }else {
            //撤销窗口请求
            count.decrementAndGet ();
            return false;
        }
    }

    /**
     * 重置窗口起始时间
     * @param now 窗口起始时间
     */
    private void resetTime(long now) {
        if (now-windowStartTime.get() > windowSizeMs){
            //设置起始时间为now
            if (windowStartTime.compareAndSet (windowStartTime.get(), now)){
                //计数器清零
                count.set (0);
            }
        }
    }

    /**
     * 测试
     */
    public static void main(String[] args)throws Exception {
        //获取限制器（时间窗口：1s 阀值：2次）
        FixedAlgorithm fixedAlgorithm = new FixedAlgorithm (1000L,2);
        for (int i = 0; i < 5; i++) {
            System.out.println (fixedAlgorithm.tryAcquire()? "确认":"拒绝");
        }

        //等待窗口过期
        Thread.sleep (1000L);
        System.out.println ("恢复窗口请求之后!");
        for (int i = 0; i < 3; i++) {
            System.out.println (fixedAlgorithm.tryAcquire ()? "确认":"拒绝");
        }
    }
}
